Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
12
Добавлен:
19.04.2024
Размер:
25.3 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

 

o

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

110 m

Академия С++

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ХАКЕР 01 /192/ 2015

 

 

 

 

 

 

m

 

 

 

w Click

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

В обоих случаях если бит знака равен 0, то число

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

положительное и 1 устанавливается для отрицатель-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ных чисел. Это правило аналогично целым числам

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

с той лишь разницей, что в отличие от целых, чтобы

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

получить число, обратное по сложению, достаточно

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

инвертировать один бит знака.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Поскольку мантисса записывается в двоичном

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

виде, подразумевается целая часть, уже равная 1,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

поэтому в записи мантиссы всегда подразумевается

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

один бит, который не хранится в двоичной записи.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

В битах мантиссы хранится именно дробная часть

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

нормализованного числа в двоичной записи.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Не нужно иметь докторскую степень, чтобы вы-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

числить точность в десятичных знаках чисел, кото-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

рые можно представить этим стандартом: 223 + 1 =

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

= 16 777 216; это явно указывает нам на тот факт,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

что

точность представления

вещественных чисел

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

с одинарной точностью достигает чуть более семи

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

десятичных знаков. Это значит, что мы не сможем

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

сохранить в данном формате, например, число

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

123 456,78 — небольшое, в общем-то, но уже начиная

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

с сотой доли мы получим не то число, что хотели. Си-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

туация усложняется тем, что для больших чисел вида

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1 234 567 890, которое прекрасно помещается даже

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

в 32-разрядное целое, мы получим погрешность уже

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

в сотнях единиц! Поэтому, например, в C++ для ве-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

щественных чисел по умолчанию используется тип

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

double. Мантисса числа с двойной точностью уже

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

превышает 15 знаков: 252+1 = 9 007 199 254 740 992

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

и спокойно вмещает в себя все 32-разрядные целые,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

давая сбой только на действительно больших 64-раз-

ПРИМЕРНОЕПЛАВАНЬЕ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

рядных целых (19 десятичных знаков), где погреш-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ность в сотнях единиц уже, как правило, несуществен-

Чтобы стало чуточку понятнее, рассмотрим пример. Закодируем число 640 (= 512 + 128)

 

 

 

 

 

 

 

 

 

 

 

 

 

на. Если же нужна большая точность, то мы в данной

в бинарном виде как вещественное число одинарной точности:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

статье обязательно в этом поможем.

• число положительное — бит знака будет равен 0;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Теперь что касается экспоненты. Это обычное би-

• чтобы получить нормализованную мантиссу, нам нужно поделить число на 512 — мак-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

нарное представление целого числа, в которое нужно

симальную степень двойки, меньшую числа, получим 640/512 = 512/512 + 128/512 или

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

возвести 2, чтобы при перемножении на мантиссу

1 + 1/4, что дает в двоичной записи 1,01, соответственно, в битах мантиссы будет

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

в нормализованном виде получить исходное число.

0100000 00000000 00000000;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Вот только в стандарте вдобавок ввели смещение,

• чтобы получить из 1 + 1/4 снова 640, нам нужно указать экспоненту, равную 9, как раз

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

которое нужно вычитать из бинарного представления,

29 = 512, число, на которое мы поделили число при нормализации мантиссы, но в би-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

чтобы получить искомую степень десятки (так назы-

нарном виде должно быть представление в смещенном виде, и для вещественных

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ваемая biased exponent — смещенная экспонента).

чисел одинарной точности нужно прибавить 127, получим 127 + 9 = 128 + 8, что в бинар-

 

 

 

 

 

 

 

 

 

 

 

 

 

Экспонента смещается для упрощения операции

ном виде будет записано так: 10001000.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

сравнения, то есть для одинарной точности берет-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ся значение 127, а для двойной 1023. Все это звучит

Для двойной точности будет почти все то же самое, но мантисса будет содержать еще

 

 

 

 

 

 

 

 

 

 

 

 

 

крайне сложно, поэтому многие пропускают главу

больше нулей справа в дробной части, а экспонента будет 1023 + 9 = 1024 + 8, то есть чуть

 

 

 

 

 

 

 

 

 

 

 

 

 

о типе с плавающей точкой. А зря!

больше нулей между старшим битом и числом экспоненты: 100 00001000. В общем, все

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

не так страшно, если аккуратно разобраться.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Задание на дом: разобраться в двоичной записи следующих констант: плюс и минус

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

бесконечность (INF — бесконечность), ноль, минус ноль и число-не-число (NaN — not-a-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

number).

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ЗАБУЙКИНЕЗАПЛЫВАЙ!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Есть одно важное правило: у каждого формата представления числа есть свои пределы,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

за которые заплывать нельзя. Причем обеспечивать невыход за эти пределы приходится

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

самому программисту, ведь поведение программы на С/С++ — это сделать невозмути-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

мое лицо при выдаче в качестве сложения двух больших положительных целых чисел ма-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ленькое отрицательное. Но если для целых чисел нужно учитывать только максимальное

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

и минимальное значение, то для вещественных чисел в представлении с плавающей точ-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

кой следует больше внимания обращать не столько на максимальные значения, сколько

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

на разрядность числа. Благодаря экспоненте максимальное число для представления

 

 

 

 

 

 

 

 

 

 

 

 

 

Мантисса записывается в дво-

с плавающей точкой при двойной точности превышает 10308, даже экспонента одинарной

 

 

 

 

 

 

 

 

 

 

 

 

 

точности дает возможность кодировать числа свыше 1038. Если сравнить с «жалкими»

 

 

 

 

 

 

 

 

 

 

 

 

 

ичном виде, и отбрасывается

1019, максимумом для 64-битных целых чисел, можно сделать вывод, что максимальные

 

 

 

 

 

 

 

 

 

 

 

 

 

и минимальные значения вряд ли когда-либо придется учитывать, хотя и забывать про

 

 

 

 

 

 

 

 

 

 

 

 

 

целая часть, заведомо рав-

них не стоит.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Другое дело проблема точности. Жалкие 23 бита под мантиссу дают погрешность уже

 

 

 

 

 

 

 

 

 

 

 

 

 

ная 1, поэтому никогда не за-

на 8-м знаке после запятой. Для чисел с двойной точностью ситуация не столь плачев-

 

 

 

 

 

 

 

 

 

 

 

 

 

ная, но и 15 десятичных знаков очень быстро превращаются в проблему, если, например,

 

 

 

 

 

 

 

 

 

 

 

 

 

бываем, что мантисса на один

при обработке данных требуется 6 фиксированных знаков после точки, а числа до точ-

 

 

 

 

 

 

 

 

 

 

 

 

 

ки достаточно большие, под них остается всего лишь 9 знаков. Соответственно, любые

 

 

 

 

 

 

 

 

 

 

 

 

 

бит длиннее, чем она хранит-

многомиллиардные суммы будут давать значительную погрешность в дробной части.

 

 

 

 

 

 

 

 

 

 

 

 

 

При большой интенсивности обработки таких чисел могут пропадать миллиарды евро,

 

 

 

 

 

 

 

 

 

 

 

 

 

ся в двоичном виде

 

просто потому, что они «не поместились», а погрешность дробной части суммировалась

 

 

 

 

 

 

 

 

 

 

 

 

 

 

и накопила огромный остаток неучтенных данных.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

 

i

 

 

 

F

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

 

t

 

P

D

 

 

 

 

 

 

 

 

 

o

 

 

P

D

 

 

 

 

 

 

 

 

o

 

 

 

 

NOW!

r

 

 

 

 

 

 

NOW!

r

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Всё, точка, приплыли!

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

ХАКЕР m01 /192/ 2015

w

 

 

 

 

 

 

 

 

 

m

w Click

 

 

w 111Click

 

 

 

 

 

o

 

w

 

 

 

 

 

 

 

 

 

o

 

 

 

w

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

 

.c

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

df

 

 

 

 

 

e

 

 

 

 

p

df

 

 

 

 

e

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

Если для целых чисел нужно учитывать только максимальное и минимальное

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

значение, то для вещественных чисел в представлении с плавающей точкой

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

следует больше внимания обращать не столько на максимальные значения,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

сколько на разрядность числа

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Если бы это была только теория! На практике

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

не должно пропадать даже тысячной доли цента, по-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

грешность всех операций должна быть строго рав-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

на нулю. Поэтому для бизнес-логики, как правило,

Всегда нужно учитывать две вещи

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

не используют C/C++, а берут C# или Python, где

при реализации операций с числа-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

в стандартной библиотеке уже встроен тип Decimal,

ми, поскольку они подразумевают

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

обрабатывающий десятичные дроби с нулевой по-

интенсивное использование: во-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

грешностью при указанной точности в десятичных

первых, нужно всегда оптимизиро-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

знаках после запятой.

вать алгоритм, сводя к минимуму

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Что же делать нам, программистам на C++, если

операций умножения и деления,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

перед нами стоит задача обработать числа очень

поэтому стоит заранее упростить

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

большой разрядности, при этом не используя высо-

выражение математически, так, что-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

коуровневые языки программирования? Да то же,

бы легко выполнялся первый пункт.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

что и обычно: заполнить пробел, создав один неболь-

В нашем случае все нужно свести

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

шой тип данных для работы с десятичными дробями

к минимуму целочисленных деле-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

высокой точности, аналогичный типам Decimal высо-

ний с остатком. Во-вторых, нужно

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

коуровневых библиотек.

обязательно проверять все возмож-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ДОБАВИМПЛАВАЮЩЕЙТОЧКЕЦЕМЕНТА

ные ситуации переполнения числа

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

с выходом за границы вычисляемо-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Пора зафиксировать плавающую точку. Поскольку мы

го типа, иначе получишь весьма не-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

решили избавиться от типа с плавающей точкой из-

очевидные ошибки при использова-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

за проблем с точностью вычислений, нам остаются

нии своего типа.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

целочисленные типы, а поскольку нам нужна макси-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

мальная разрядность, то и целые нам нужны макси-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

мальной разрядности в 64 бита.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Сегодня

в учебных целях мы рассмотрим,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

как создать

представление вещественных чисел

ОПЕРАЦИИСТИПОМДЕСЯТИЧНОЙДРОБИ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

с гарантированной точностью до 18 знаков после

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

точки. Это достигается простым комбинированием

Разумеется, тип числа с повышенной точностью будет

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

двух 64-разрядных целых для целой и дробной ча-

бесполезен без арифметических операций. Сложе-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

сти соответственно. В принципе, никто не мешает

ние реализуется сравнительно просто:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

вместо одного числа для каждой из компонент взять

x = a + b * 10–18,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

массив значений и получить полноценную «длинную»

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

арифметику. Но будет более чем достаточно сейчас

y = c + d * 10–18,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

решить проблему точности, дав возможность рабо-

z = x + y = e + f * 10–18,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

тать с точностью по 18 знаков до и после запятой,

a, c, e: int64_t;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

зафиксировав точку между двумя этими значениями

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

и залив ее цементом.

b, d ,f: uint64_t;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ОТСЫПЬИМНЕДЕЦИМАЛА!

 

 

0 <= b, d, f < 1018,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

z = (a + b * 10–18) + (c + d * 10–18)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Сначала немного теории. Обозначим наши две компоненты, целую и дробную часть чис-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ла, как n и f, а само число будет представимо в виде

e = a + c + [b * 10–18 + d * 10–18]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

x = n + f * 10–18,

де n, f —

ел е, 0 <= f < 1018.

f = {b * 10–18 + d * 10–18} * 1018

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Здесь [n] — это получение целой части числа,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Для целой части лучше всего подойдет знаковый тип 64-битного целого, а для дроб-

а {n} — получение дробной части. Все бы хорошо,

 

 

 

 

 

 

 

 

 

 

 

 

 

ной — беззнаковый, это упростит многие операции в дальнейшем.

но вспоминаем про ограничение целых чисел. Зна-

 

 

 

 

 

 

 

 

 

 

 

 

 

class decimal

 

 

чение 1018 уже близко к грани значений беззнаково-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

го 64-битового целого типа uint64_t (потому мы его

 

 

 

 

 

 

 

 

 

 

 

 

 

{

 

 

 

...

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

private:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

int64_t m_integral;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

};

 

uint64_t m_fractional;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Целая часть в данном случае — максимальное целое, меньшее представляемого чис-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ла, дробная часть — результат вычитания из этого числа его целой части, помноженный

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

на 1018 и приведенный к целому: f = (x – n) * 1018.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Целая часть для отрицательных чисел получится большей по модулю самого чис-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ла, а дробная часть будет не совпадать с десятичной записью самого числа, например

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

для числа –1,67 компонентами будут: n = –2 и f = 0,33 * 1018. Зато такая запись позволя-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ет упростить и ускорить алгоритмы сложения и умножения, поскольку не нужно ветвления

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

для отрицательных чисел.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

 

 

F

 

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

 

i

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

to

112

 

 

Академия С++

w Click

 

 

m

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

 

g

 

 

 

 

 

 

 

df

 

 

 

n

e

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

 

и выбрали), но нам никто не мешает чуточку упростить выражение, чтобы гарантированно оставаться в границах типа, исходя из начальных условий:

e = a + c + (b + d) div 1018, f = (b + d) mod 1018.

Разумеется, стоит проверить граничные значения при сложении a и c. Также, исходя из того, что b и d меньше 1018, мы знаем, что (b + d) < 2 * 1018, значит, последним сложением добавится максимум единица, поэтому алгоритмически деление можно вообще не считать для оптимизации скорости выполнения операций:

e = a + c;

f = b + d;

if (f >= 1018) f -= 1018, ++e;

Здесь опущены проверки на максимальное целое для значения e для простоты изложения.

Для вычитания все чуть-чуть сложнее, здесь уже нужно учитывать переход ниже нуля для беззнакового целого. То есть нужно сравнить два числа до вычитания.

e = a - c;

if (b >= d) f = b - d;

else f = (1018 - d) + b, --e;

В целом все пока несложно. До умножения и деления все всегда несложно.

УМНОЖЕНИЕЧИСЕЛСФИКСИРОВАННОЙТОЧНОСТЬЮ

Рассмотрим сперва умножение. Поскольку числа в дробной части довольно большие и, как правило, находятся в ближайшей окрестности 1018, нам придется либо использовать язык ассемблера и операции с Q-регистрами, либо пока обойтись целыми числами, побив каждую компоненту на две части по 109. В этом случае умножение будет давать не больше 1018 и ассемблер нам пока не понадобится, будет не так шустро, но нам хватит 64-разрядного целого, и мы останемся внутри C++.

Ты помнишь школу и умножение столбиком? Если нет, самое время вспомнить:

a = s * a - a * 10-9; b = b - b * 10-9;

a

1

2

1

2

c = s

* c - c * 10-9; d = d - d * 10-9;

c

1

2

1

2

0 <= a

, b , c , c < 109;

 

2

2

1,2

1,2

 

sa,c = sign(a), sign(c)

 

0 <= a1, 1

< MAX_INT64 / 109

 

Введем матрицу для упрощения вычисления умножения:

U = (a1, a2, b1, b2),

V = (c1, c2, d1, d2)T,

A = V * U,

| a1*c1 a1*c1 b1*c1 b2*c1 | A = | a1*c2 a1*c2 b1*c2 b2*c2 | | a1*d1 a1*d1 b1*d1 b2*d1 | | a1*d2 a1*d2 b1*d2 b2*d2 |

Матрица вводится не столько для удобства вычисления, сколько для удобства проверки. Ведь A11 = a1 * c1 должно быть не больше MAX_INT64 / 1018, а значения диагональю ниже: A12 = a1 * c2 и A21 = a2 * c1 должны быть не больше MAX_INT64 / 109. Просто потому, что мы будем умножать на эти коэффициенты при сложении компонент:

e = A11*1018 + (A12+A21)*109 + (A13+A22+A31) + (A14+A23+A32+A41) div 1018, f = (A14+A23+A32+A41) mod 1018 + (A24+A33+A42) + (A34+A43) div 109

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

ХАКЕР 01 /192/ 2015

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Здесь мы опускаем слагаемое A44 div 1018 просто потому, что оно равно нулю.

Разумеется, перед каждым сложением стоит проверить, не выйдем ли мы за пределы MAX_INT64. К счастью, мы можем оперировать беззнаковым типом uint64_t для всех компонент матрицы и для промежуточного результата. Все, что нужно будет сделать в конце, — это

определить

знак результата

se = sa xor

sc и для отрица-

тельного числа поправить целую и дробную часть: целую уменьшить на единицу, дробную вычесть из единицы. Вот, в общем, и все умножение, главное — быть очень аккуратным. С ассемблером все на порядок проще, но этот материал выходит за рамки Академии

C++.

АЛГОРИТМДЕЛЕНИЯ БЕЗРЕГИСТРАЦИИИСМС

Если ты помнишь алгоритм деления столбиком — молодец, но здесь он будет не нужен. Благодаря математике и небольшому колдовству с неравенствами нам будет проще посчитать обратное число x–1 и выполнить умножение на x–1.

Итак, решаем задачу

y = x–1 = 1/(a + b * 10–18) = c + d * 10–18

Для упрощения рассмотрим нахождение обратного числа для положительного x.

Если хотя бы одна из компонент x равна нулю (но не обе сразу), вычисления сильно упрощаются.

Если a = 0, то:

y = 1 / (b * 10–18) = 1018 / b, e = 1018 div b,

f = 1018 mod b;

если b =

0, a

= 1, то y = e = 1, f = 0;

ecли b = 0, a > 1, то:

y =

1 /

a,

 

e =

0, f

= 1018 div a.

Для более общего случая, когда x содержит ненулевые дробную и целую части, в этом случае уравнение сводится к следующему:

если a > 1, b != 0, то:

y = 1 / (a + b * 10–18) < 1,

отсюда e = 0,

f = 1018 / (a + b * 10–18).

Теперь нужно найти максимальную степень 10, которая будет не больше a, и итерационно выполнять следующее действие:

k = max(k): 10k <= a,

u = 1018, v = (a * 1018-k + b div 10k);

f = (u / v) * 1018-k,

for (++k; k <=18; ++k)

{

u = (u % v) * 10;

if (!u) break; // дал е уду нули

f += u / v * 1018-k;

}

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

ХАКЕР m

01 /192/ 2015

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

Всё, точка, приплыли!

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w113Click

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Здесь мы всего лишь используем умножение и деление дроби на одинаковый множитель — степень десятки, а затем пошагово вычисляем деление и остаток от деления для очередной степени десятки.

Очень полезно будет завести массив степеней десяток от 0 до 18 включительно, поскольку вычислять их совершенно излишне, мы их знаем заранее и требоваться они нам будут часто.

ПРЕОБРАЗОВАНИЯТИПОВ

Мы знаем и умеем достаточно, чтобы теперь превратить расплывчатые float и double в наш новенький decimal.

decimal::decimal(double value)

:m_integral(static_cast<int64_t>(std::floor(value)) m_fractional(static_cast<int64_t>(std::floor(

(value - m_integral) * 1018 + 0.5))

{

 

 

 

normalize();

 

 

}

 

 

 

void decimal::normalize()

 

 

{

 

 

 

uint64_t tail = m_fractional % 103;

 

 

 

if (tail)

 

 

{

 

 

 

if (tail > 103/2)

 

 

 

m_fractional += 103 - tail;

 

 

 

else

 

GITHUB

 

m_fractional -= tail;

 

}

 

 

НЕУПЛЫВАЙ,ИТОЧКА!

}

 

Со статьей идет несколь-

В заключение скажу лишь то, что подобный тип в C/C++ мо-

 

 

ко файлов с исходниками

жет появиться в весьма специфической задаче. Как правило,

Здесь 103 является, по сути, той погреш-

одной из возможных

проблемы чисел с большой точностью решаются языками

ностью, за которой double перестает быть

реализаций decimal,

типа Python или C#, но если уж понадобилось по 15–18 знаков

точным. При желании погрешность можно

а также с небольшим

до запятой и после, то смело используй данный тип.

еще уменьшить, здесь 1018-15 нужно для на-

тестом вещественных

Получившийся тип decimal решает проблемы с точно-

глядности изложения. Нормализация после

чисел для лучшего усво-

стью вещественных чисел и обладает большим запасом воз-

преобразования нужна будет все равно, по-

ения материала.

можных значений, покрывающим int64_t. С другой стороны,

скольку точно double заведомо ниже даже

 

типы double и float могут принимать более широкий интервал

дробной части decimal. Кроме того, нужно

 

значений и выполняют арифметические операции на уровне

учитывать случай, когда double выходит

 

команд процессора, то есть максимально быстро. Старайся

 

за пределы int64_t, при таких условиях наш

 

обходиться аппаратно поддерживаемыми типами, не залезая

decimal не сможет правильно преобразовать

 

в decimal лишний раз. Но и не бойся использовать данный

целую часть числа.

 

тип, если есть необходимость в точном вычислении без по-

Для float все выглядит похожим образом, но погрешность на порядок выше:

терь.

1018-7 = 1011.

 

 

В помощь также знания о двоичном представлении чисел

Все целые числа преобразовываются в decimal без проблем, просто ини-

с плавающей точкой, полученные в этой статье. Зная плюсы

циализируя поле m_integral. Преобразование в обратную сторону для целых

и минусы формата типов double и float, ты всегда примешь

чисел также будет просто возврат m_integral, можно добавить округление m_

правильное решение, какой тип пользовать. Ведь, возмож-

fractional.

 

но, тебе и вовсе требуется целое число, чтобы хранить массу

Преобразование из decimal в double и float сводится к вышеуказанной фор-

не в килограммах, а в граммах.

муле:

 

Будь внимателен к точности, ведь точность наверняка вни-

return m_integral + m_fractional * 10-18;

 

мательна к тебе!

 

 

Отдельно стоит рассмотреть преобразование в строку и из строки.

 

Целочисленная часть, по сути, преобразуется в строку как есть, после этого

 

остается только вставить decimal separator и вывести дробную часть как целое,

 

отбросив завершающие нули. Также можно ввести поле «точность» m_precision

 

и записывать в строку лишь указанное в нем число десятичных знаков.

 

Чтение из строки то же, но в обратную сторону. Здесь сложность лишь в том,

 

что и знак, и целая часть, и разделитель дробной и целой части, и сама дробная

 

часть — все они являются опциональными, и это нужно учитывать.

 

В общем и целом я предоставляю полную свободу при реализации этого

 

класса, но на всякий случай со статьей идет несколько файлов с исходниками од-

 

ной из возможных реализаций decimal, а также с небольшим тестом веществен-

 

ных чисел для лучшего усвоения материала.

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

114 m

Unixoid

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

ЗНАКОМИМСЯ

Мартин «urban.prankster» С КЕШИРУЮЩИМ

Пранкевич ПРОКСИ VARNISH martin@synack.ru

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

ХАКЕР 01 /192/ 2015

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Syaheir Azizan@shutterstock.com

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

ХАКЕР m

01 /192/ 2015

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

Полировщик для веб-сайта

Обработка большого числа запросов может быть весьма тяжелой задачей, забирающей ресурсы веб-сервера. Чтобы снять нагрузку, используют фронтенд, в качестве которого могут выступать легкие nginx/lighttpd или кеширующий прокси-сервер. Во втором качестве очень популярен Squid, он универсален, но не всегда оптимален. И именно в этой задаче его более эффективно заменяет Varnish.

НЕМНОГООПРОЕКТЕ

Varnish (varnish-cache.org) представляет со-

бой кеширующий «обратный» (reverse) прок- си-сервер и акселератор HTTP. Принцип его работы в общем стандартен для такого класса программ. Он получает запрос, обрабатывает его и сразу выдает ответ, если он присутствует в кеше; если нет, то обращается к веб-серверу за результатом. Ответ помещается в кеш. Varnish написан с нуля для норвежской газеты Verdens

Проект уже предостав- Gang. Версия 1.0 появилась в 2006 году, в 2014-м ляет большое количе- представлен релиз 4.0. Основной код доступен ство модулей под BSD-подобной лицензии, но есть и коммер-

ческие модули.

Проект сразу привлек к себе внимание весьма громкими заявлениями автора, одного из разработчиков FreeBSD Пола-Хеннинга Кам-

па (Poul-Henning Kamp) о том, что все (с наме-

ком на Squid) сделано неправильно. И действительно, Varnish выделяет современный дизайн, эффективно использующий возможности современных многопроцессорных систем (Squid научился работать с SMP чуть позже с версии 3.2). Многопоточность реализована с помощью стандартных потоков POSIX, их количество регулируется. Это одна из причин, почему Varnish

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w115Click

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

116 m

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

.c

 

 

p

 

 

 

 

g

 

 

 

 

df

 

 

n

e

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

 

 

 

 

F

 

 

 

 

 

 

 

t

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

P

 

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

Unixoid

 

 

 

 

 

to

 

 

 

 

 

 

 

ХАКЕР 01 w Click

 

 

 

 

 

m

 

 

 

 

/192/ 2015

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

 

 

 

 

p

 

 

 

 

 

g

 

 

 

 

 

 

 

 

 

df

 

 

 

n

e

 

 

 

 

 

 

 

 

 

-x cha

 

 

 

 

не очень хорошо работает в Windows. Каждый

 

ляет производить с HTTP-трафиком практически любые мани-

 

 

 

 

 

 

запрос обрабатывается в отдельном потоке.

 

пуляции, которые можно ограничить только собственным во-

 

 

 

 

 

 

Причем с версии 4.0 за получение запроса

 

ображением. Поддерживается балансировка нагрузки (round

 

 

 

 

 

 

от пользователя и передачу запроса серверу от-

 

robin, random и DNS, Client IP). Возможности расширяются

 

 

 

 

 

 

вечают разные потоки, что еще более повысило

 

при помощи модулей, называемых VMOD (Varnish MODules).

 

 

 

 

 

 

производительность. Varnish поддерживает тех-

 

Проект предоставляет необходимую документацию, позволя-

 

 

 

 

 

 

нологию ESI (Edge Side Includes), позволяющую

 

ющую написать такой модуль самостоятельно. Часть модулей

 

 

 

 

 

 

разбивать веб-страницу на части и запрашивать

 

(varnish-cache.org/vmods) уже включены в стандартную по-

 

 

 

 

 

 

их отдельно. Кеш может хранить любую инфор-

 

ставку, некоторые доступны в виде концепта или находятся

 

 

 

 

 

 

мацию. В итоге Varnish отлично подходит для ке-

 

в разработке.

 

 

 

 

 

 

 

 

 

 

 

ширования динамического контента.

 

Сегодня Varnish используют такие веб-сервисы,

 

 

 

 

 

 

Для хранения данных (кеша, журналов опера-

 

как Facebook, Twitter, Vimeo и Tumblr.

 

 

 

 

 

 

 

 

 

 

 

ций) используется виртуальная память. Управле-

 

УСТАНОВКАИБАЗОВАЯНАСТРОЙКАVARNISH

 

 

 

 

 

 

 

 

 

 

 

нием того, что выгружается на диск, занимается

 

 

 

 

 

 

 

 

 

 

 

 

ОС. Здесь авторы Varnish справедливо считают,

 

Официально рекомендуется установка Varnish на современных

 

 

 

 

 

 

что разработчики ОС свое дело знают, а дубли-

 

версиях x64-битных Linux, FreeBSD или Solaris. Пакеты можно

 

 

 

 

 

 

рование только ухудшает производительность.

 

найти в дополнительных репозиториях (вроде EPEL или Ubuntu

 

 

 

 

 

 

В отличие от Squid, который изначально боль-

 

Universe) большинства дистрибутивов Linux и портах *BSD-

 

 

 

 

 

 

ше ориентировался на кеширование клиентских

 

систем. Сам проект предоставляет репозитории и подробные

 

 

 

 

 

 

запросов, Varnish был разработан и оптимизиро-

 

инструкции для Red Hat, Debian, Ubuntu и FreeBSD. В большин-

 

 

 

 

 

 

ван именно в качестве ускорителя HTTP и ничего

 

стве случаев следует использовать именно репозиторий разра-

 

 

 

 

 

 

другого больше не умеет. Мы не найдем здесь

 

ботчика, так как в нем находится более свежая версия продукта.

 

 

 

 

 

 

поддержку остальных протоколов (FTP, SMTP

 

Возможна работа и в Windows (через Cygwin), но оптимизация

 

 

 

 

 

 

и прочие), не увидим возможности прямого

 

под *nix-системы не гарантирует максимальной производи-

 

 

 

 

 

 

прокси — кеширования веб-страниц для эконо-

 

тельности результата. На сайте доступны две версии Varnish —

 

 

 

 

 

 

мии внешнего трафика (Varnish «привязывается»

 

3.x и 4.x, обе являются стабильными, но поддержка линейки 3.x

 

 

 

 

 

 

к бэкендам). Естественно, отличаются и возмож-

 

будет прекращена в первой половине 2015 года. Кстати, код до-

 

 

 

 

 

 

ности по конфигурированию.

 

статочно хорошо написан, поэтому исправлений вносится мало

 

 

 

 

 

 

Язык

конфигурации

Varnish Configuration

 

(например, 3-я ветка, появившаяся в июне 2011-го, содержала

 

 

 

 

 

 

Language (VCL) — динамический, скрипт сам

 

всего шесть версий), можно не бояться использовать свежие

 

 

 

 

 

 

по себе по сути является отдельным плагином.

 

релизы. Стандартно Varnish ставится перед веб-сервером и ке-

 

 

 

 

 

 

Код транслируется в С (можно сразу писать

 

ширует запросы. В нагруженных системах иногда используют

 

 

 

 

 

 

встраиваемый код на С), после чего инструкции

 

более сложный вариант, когда вначале запрос принимает лег-

 

 

 

 

 

 

компилируются в библиотеку и подгружаются

 

кий веб-сервер (nginx, lighttpd), который умеет быстро отдавать

 

 

 

 

 

 

в память. Можно вносить изменения в конфи-

 

определенные страницы. При необходимости этот сервер че-

 

 

 

 

 

 

гурацию на лету. Инструкции в VCL позволяют

 

рез Varnish обращается к основному HTTP-серверу, генериру-

 

 

 

 

 

 

кешировать только определенные запросы,

 

ющему контент. Varnish при наличии информации в кеше отдает

 

 

 

 

 

 

снижая нагрузку при генерации динамических

 

ее оттуда.

 

 

 

 

 

 

 

 

 

 

 

объектов, блокировать доступ к определенным

 

Для примера установим Varnish в Ubuntu 14.04 LTS в каче-

 

 

 

 

 

 

каталогам

и скриптам,

подменять заголовки

 

стве фронтенда Apache. В других дистрибутивах отличия толь-

 

 

 

 

 

 

и многое другое. Есть и механизм проверки ра-

 

ко в расположении конфигурационных файлов и особенностях

 

 

 

 

 

 

ботоспособности бэкендов (замер времени от-

 

пакетных систем.

 

 

 

 

 

 

 

 

 

 

 

вета, счетчик неудачных проверок и так далее),

 

$ sudo apt-get install apt-transport-https curl

 

 

 

 

 

 

 

 

 

 

 

возможность перезаписи и перенаправления

Файл /etc/default/

 

 

 

 

 

 

 

 

 

 

 

(rewrite) запросов. Вообще, такой подход позво-

varnish

$ sudo curl https://repo.varnish-cache.org/

 

 

 

 

 

 

 

 

 

 

 

ubuntu/GPG-key.txt | apt-key add -

$ sudo echo "deb https://repo.varnis

cache.org/ubuntu/ trusty varnish-4.0"

>> /etc/apt/sources.list.d/varnis

cache.list

$ sudo apt-get update

$ sudo apt-get install varnish

Вот, собственно, и все. После установки Varnish стартует и что-то там кеширует. Займемся конфигурированием. Установки параметров запуска демона производятся в файле /etc/ default/varnish. Первоначально он сконфигурирован с некоторыми параметрами. Если открыть файл, то увидим внутри три готовые настройки: minimal, c VCL и advanced.

По умолчанию активирован второй режим. При этом сервер принимает подключения на порт 6081, для администрирования используется localhost:6082, а бэкенд определяется в VCL. Для кеширования выделяется 256 Мб памяти. Настроим так, чтобы Varnish слушал 80-й порт, кешируя запросы HTTP-сервера, расположенного на этой же машине. Комментируем настройки второго варианта и в качестве шаблона будем использовать advanced, как более наглядный.

$ sudo nano /etc/default/varnish

# а у а де он varnishd и за уз е

и е

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

 

 

 

 

 

 

F

 

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

 

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

ХАКЕР m01 /192/ 2015

 

 

 

Полировщик для веб-сайта

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

START=yes

 

 

 

 

 

 

 

 

 

 

 

 

 

#

 

 

 

а

и ал ное

оли е

во о

 

 

 

 

 

 

 

 

 

айлов (дл

ulimit -n)

 

 

 

 

 

 

 

 

 

NFILES=131072

 

 

 

 

 

 

 

 

 

 

 

#

 

 

 

а

и ал ное

оли е

во зало енной

 

 

 

 

 

 

 

 

а

 

и (for ulimit -l) дл

ло и ов и

 

 

 

 

 

 

 

аздел е ой

а

и ло а

 

 

 

 

 

 

 

 

 

#

 

 

 

и нео

 

оди о

и

ледуе

увели и

 

 

 

 

 

 

 

аз е

 

 

 

 

 

 

 

 

 

 

 

 

 

 

MEMLOCK=86000

 

 

 

 

 

 

 

 

 

 

 

#

 

 

 

о у ол ани

зе

л

е ве а

 

 

 

 

 

 

 

 

олу ае

и

е у е о узла.

и за у е

 

 

 

 

 

 

 

не

 

ол

и

 

зе

л

ов

е о

о но

 

 

 

 

 

 

 

 

е ео

едели

и

о о и -n или

 

 

 

 

 

 

INSTANCE=$(uname -n)

 

 

 

 

 

 

 

 

 

 

#

 

 

 

о у ол ани

лу а

 

в е

 

 

 

 

 

 

 

 

ин е

ей

, но

о е

у аза

IP

 

 

 

 

 

 

# VARNISH_LISTEN_ADDRESS= о

,

 

 

 

 

 

 

 

на

 

о о о

 

ини а

 

 

 

 

 

 

 

 

 

 

 

од л

ени

 

 

 

 

 

 

 

 

 

 

 

 

VARNISH_LISTEN_PORT=80

 

 

 

# д ин-ин е ей IP и о

VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1

VARNISH_ADMIN_LISTEN_PORT=6082

# VCLайл

VARNISH_VCL_CONF=/etc/varnish/default.

vcl DAEMON_OPTS="\

-a ${VARNISH_LISTEN_

ADDRESS}:${VARNISH_LISTEN_PORT} \

-f ${VARNISH_VCL_CONF} \

-T ${VARNISH_ADMIN_LISTEN_

ADDRESS}:

${VARNISH_ADMIN_LISTEN_PORT} \

Это основные установки. С остальными можно познако-

Настройки в default.vcl

статочно шаблонов (github.com/mattiasgeniar/

миться в файле или в документации проекта. Перестраиваем

 

varnish-3.0-configuration-templates) под разные

Apache, чтобы он слушал 8080-й порт. В зависимости от теку-

 

ситуации, подготовленных самими пользовате-

щих установок это можно сделать в разных файлах, в общем

 

лями. Они будут хорошим подспорьем при из-

идея выглядит так:

 

учении возможностей VCL.

$ sudo nano /etc/apache2/ports.conf

 

 

Varnish способен работать с несколькими

 

HTTP-серверами (бэкендами). Каждый опреде-

NameVirtualHost *:8080

 

 

ляется аналогично примеру выше:

Listen 8080

 

backend server1 {

 

 

 

Теперь осталось указать Varnish, где находится HTTPсервер. В /etc/default/varnish это можно сделать при помощи ключа -b (-b localhost:8080), но удобнее для этого использовать VCL-файл, который указан в переменной VARNISH_VCL_ CONF (в сырцах есть пример example.vcl). Открываем и смотрим, чтобы внутри была инструкция:

$sudo nano /etc/varnish/default.vcl backend apache {

.host = "127.0.0.1";

.port = "8080";

}

Минимальные установки готовы. Перезапускаем Varnish:

.host = "10.0.0.11";

}

backend server2 {

.host = "10.0.0.12";

}

После чего можно строить правила, указывая серверы по имени. Если бэкенды равнозначны и используются только для распределения нагрузки, то достаточно сообщить об этом Varnish с помощью директивы director:

director balanced_servers round-robin {

{

.backend = server1;

$ sudo service varnish start

Проверяем при помощи netstat, слушается ли порт и доступен ли веб-сервер.

ПРОДВИНУТЫЕНАСТРОЙКИ

Это самый простой пример, и, как видим, заставить Varnish кешировать запросы очень легко, но он пока не «разбирается» и не вмешивается в трафик. Познакомившись с VCL, можно расширить базовые возможности. Параметров, которые можно настроить, очень много и, чтобы их описать, потребуется книга. VCL — это язык программирования, в котором найдем все, что положено: переменные, функции, комментарии и прочее. Вариантов использования много, и с ходу их освоить не получится. В качестве отправной точки можно рекомендовать документацию проекта, в частности Varnish Book (varnish-software.com/static/book). Также в Сети уже есть до-

}

{

.backend = server2;

}

}

Можно также использовать случайный выбор — для этого вместо round-robin прописываем random. Возможна реализация и более сложных алгоритмов. Но для этого нужно познакомиться с возможностями VCL-файлов. В default.vcl мы увидим несколько именованных

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w117Click

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

118 m

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

.c

 

 

p

 

 

 

 

g

 

 

 

 

df

 

 

n

e

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

 

F

 

 

 

 

 

 

t

 

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

r

 

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Unixoid

 

 

 

 

 

to

 

 

 

 

 

ХАКЕР 01

w Click

 

 

 

 

 

m

 

 

/192/ 2015

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

-x cha

 

 

 

 

блоков (функций). По умолчанию параметры

Список команд

внутри отсутствуют, вот именно с их помощью

varnishadm

и производится тонкая настройка кеширования.

 

Кое-что можно установить, не вникая в работу

 

приложений на веб-сервере, в более сложных

 

случаях потребуется глубокий анализ выдавае-

 

мых страниц (проект предоставляет несколько

 

утилит, о которых дальше).

 

Разберем некоторые из них. Порядок вызова

 

функций наглядно показан в разделе VCL Basics

 

(varnish-software.com/static/book/VCL_Basics.

 

html), отдельные вопросы конфигурирования

 

есть в документации (varnish-cache.org/docs).

 

Списки ACL позволяют управлять доступом

 

к определенным URL или вручную распределять

 

или обрабатывать запросы некоторых клиентов.

 

Создадим правило, которое включает все ло-

 

кальные узлы.

 

acl local {

"localhost";

"192.168.1.0"/24;

! "192.168.1.10";

}

Как видим, разрешается инвертирование при помощи !, то есть 192.168.1.10 не попадает под правило. Теперь к этому списку можем обратиться при помощи конструкции (client.ip ~ local).

Функция vcl_recv вызывается при получении запроса и перед проверкой данных в кеше. Именно здесь можно модифицировать запрос, удалить cookie, произвести нормализацию, выбрать бэкенд в зависимости от запроса. По умолчанию Varnish не кеширует запросы с установленными cookie. Для статических страниц лучше активировать такую возможность, для этого просто вырезаем cookie. Также для примера запретим доступ к файлам cron.php и install.php для всех узлов, кроме локальных, и скажем Varnish, чтобы он перенаправлял все запросы к update.php сразу на бэкенд.

sub vcl_recv {

set req.backend = apache

if (req.url ~

"\.(css|js|png|gif|jp(e)?g)")

{

unset req.http.cookie;

}

return (lookup);

if (req.url ~ "^/(cron|install)\.

php$" && !client.ip ~ local)

{

error 404 "Page not found.";

}

if (req.url ~ "^/update\.php$"

return (pass);

}

}

Это, конечно, не все, что можно сделать. Ис-

пользуя req.http.User-Agent, можем распреде-

лять бэкенды в зависимости от браузера/устройства клиента. Подробнее: varnish-cache.org/ docs/trunk/users-guide/devicedetection.html.

Функция return в случае с vcl_recv может принимать аргументы: lookup — указывает на необходимость поиска в кеше, pass — сразу отправляет запрос на бэкенд. Последнее, как видим, позволяет избежать кеширования определенных типов файлов, например медиа или постоянно обновляющегося контента.

Некоторые функции вызывают пустыми, просто чтобы пропустить определенную проверку, указав нужный код возврата. Кроме указанных выше, возможны варианты: deliver, fetch, hash,

pipe, error, restart, retry. Функция vcl_hash позволяет определить уникальность запроса, который будет кешироваться. По умолчанию хеш формируется на основании URL и IP/имени сервера. В большинстве случаев этого достаточно, но в некоторых ситуациях этого не хватает и, возможно, потребуется изменение правил. Например, для определения уникальности используют сookie.

Функция vcl_error позволяет генерировать контент, не обращаясь к веб-серверу. Используется для выдачи сообщений об ошибках и редиректа. Далее в зависимости от ситуации за-

прос обрабатывается vcl_fetch, vcl_pass и vcl_miss. В 4.0 они заменены на более удобные функции vcl_backend_fetch и vcl_ backend_response, которые вызываются перед передачей запроса серверу и после получения ответа соответственно.

В следующем примере мы убираем выставление cookie для файлов изображений, а также задаем время жизни кешированного содержимого для этих файлов в один час.

sub vcl_backend_response {

if (bereq.url ~ "\.(png|gif|jpg)$") {

unset beresp.http.set-cookie;

set beresp.ttl = 1h;

}

}

И перед отправкой полученных от бэкенда данных вызывается функция vcl_deliver. В простейшем случае она пустует, то есть ответ передается без изменений. Но здесь можно при желании модифицировать заголовки. Для примера просто удалим все упоминания о Varnish:

sub vcl_deliver {

remove resp.http.X-Varnish;

remove resp.http.X-Powered-By;

}

Это, конечно, далеко не все возможности, но нужно идти дальше.

УПРАВЛЕНИЕVARNISH

Работой Varnish можно управлять и отслеживать результат. Для этого в поставке идет несколько утилит, начинаю-

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

ХАКЕР m

01 /192/ 2015

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

Полировщик для веб-сайта

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w119Click

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

щихся с varnish*. Все они описаны в разделе «Appendix A:

Интерфейс Varnish

вести все описанные операции и получать стати-

Varnish Programs» Varnish Book. Основной утилитой является

Administration Console

стику в наглядном виде.

varnishadm. Именно с ее помощью производится админи-

 

Правда, есть минус — доступен он только

стрирование, просмотр статуса и ошибок, загрузка модулей

 

в коммерческой версии Varnish Plus. Бесплатное

на лету. Принцип работы очень прост. Вызываем:

 

ПО пока небогато вариантами. Плагины, поддер-

$ sudo varnishadm

 

живающие Varnish, есть в системах мониторинга

 

Collectd, Nagios, Cacti и других. Список извест-

 

 

 

ных проектов можно найти на сайте varnish-

После чего появится приглашение Varnish CLI. Чтобы полу-

 

cache.org/utilities.

чить список команд, следует ввести help.

 

 

Команд немного (23), значение большинства понятно

 

 

из названия. Подробности по каждой можно получить, введя

 

 

«help команда». Например, группа команд vcl.* позволяет про-

 

 

сматривать модули и управлять их загрузкой-выгрузкой. Смо-

 

 

трим список модулей:

 

 

varnish> vcl.list

 

 

 

Команды param.show и param.set позволяют просма-

 

 

тривать и изменять параметры сервиса, panic.show и panic.

 

 

clear — отображать и очищать ошибки, ban и ban.list — указы-

 

 

вать страницы, не подлежащие кешированию.

 

 

Две утилиты varnishtop и varnishhist очень помогают при пер-

 

 

воначальной настройке, так как позволяют просмотреть список

 

 

наиболее часто встречающихся параметров (URL, идентифи-

 

 

каторы, статус и так далее). Первая утилита выводит их в виде

 

 

top, вторая как гистограмму. При запросе можно использовать

 

 

регулярные выражения, поэтому потеряться в большом количе-

 

 

стве страниц невозможно. Например, просмотрим список наи-

 

ВЫВОД

более запрашиваемых URL и заголовки:

График загрузки

$ varnishtop -i RxUrl

 

Varnish в Collectd

Varnish — это очень гибкая программа, позволя-

 

ющая управлять кешированием трафика с веб-

$ varnishtop -i RxHeader

 

ресурсов. Большие возможности подразумевают

 

 

 

большое количество настроек. Поэтому неко-

Еще три очень полезные утилиты, позволяющие про-

 

торое время придется затратить на их изучение

смотреть статистику (varnishstat) и информацию в журналах

 

и подгонку под конкретные условия. Но результат

(varnishlog и varnishncsa).

 

того стоит.

Скрипт varnishtest дает возможность проверить работу

 

 

кеша Varnish. Для тех, кто не хочет разбираться с командной

 

 

строкой, разработчики предлагают веб-интерфейс Varnish

 

 

Administration Console (varnish-software.com/resources/vac-

 

 

demo) — очень удобный инструмент, предлагающий произ-

 

 

Соседние файлы в папке журнал хакер